/* @flow */

import type Watcher from './watcher'
import { remove } from '../util/index'

let uid = 0

/**
 * A dep is an observable that can have multiple
 * directives subscribing to it.
 * 该类型生成的对象用于收集监听对象，个人取名收集者对象
 */
export default class Dep {
  static target: ?Watcher;
  /**
   * id的作用是什么？
   * 1）在watcher中会根据Dep.id判断当前watcher是否已经存在该dep对象。
   */
  id: number;
  subs: Array<Watcher>;

  constructor () {
    this.id = uid++
    this.subs = []
  }

  /**
   * 该方法是在watcher中调用的，用于将watcher对象添加到当前dep对象中。
   * @param sub
   */
  addSub (sub: Watcher) {
    this.subs.push(sub)
  }

  removeSub (sub: Watcher) {
    remove(this.subs, sub)
  }

  /**
   * 该方法用于将收集者对象添加到监听者对象中。在Dep.target对象（也就是watcher对象）
   * 的addDep方法中，会调用收集者dep对象的addSub将watcher对象添加到收集者对象中。也就是说
   * 收集者对象dep与监听者对象watcher是相互引用的。
   * 注意，收集者对象与监听者对象是多对多关系，收集者对象中有多个监听者对象自不必多说，
   * 那何时监听者对象中会存在多个收集者对象，比如：
   * 1）对于监听整个dom重新渲染的_watcher对象，就可能会对应多个dep对象。
   * 2）computed属性的watcher也可能存在多个dep对象
   */
  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }

  notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

// the current target watcher being evaluated.
// this is globally unique because there could be only one
// watcher being evaluated at any time.
Dep.target = null
const targetStack = []

export function pushTarget (_target: Watcher) {
  if (Dep.target) targetStack.push(Dep.target)
  Dep.target = _target
}

export function popTarget () {
  Dep.target = targetStack.pop()
}
